'use strict'

/**
 *  fullscreen (全屏控件)
 * 
 *  @author MiiKio
 *  @version 1.1.0
 *  @updatetime 2024.4.10
 *  @property {Boolean}     isEnabled     全屏控件是否可用
 *  @property {Boolean}     isFullscreen  当前是否已全屏
 *  @property {Boolean}     proxyKeydown  当前是否已代理F11按键切换全屏
 *  @property {HTMLElement} element       当前全屏的元素
 *  @property {Function}    request       启用全屏。参数：(element: 变为全屏的元素，默认为HTML根元素, options: 全屏配置)，返回 Promise<any>
 *  @property {Function}    exit          退出全屏。返回 Promise<any>
 *  @property {Function}    toggle        切换全屏。参数：(valid: 是否全屏，默认为切换, element: 变为全屏的元素，仅切换为全屏时生效, options: 全屏配置，仅切换为全屏时生效)，返回 Promise<any>
 *  @property {Function}    onchange      全屏状态更变成功的回调 (仅赋值更改)
 *  @property {Function}    onerror       全屏状态更变出错的回调 (仅赋值更改)
 *  @property {Function}    on            绑定全屏状态更变成功/出错的回调事件。参数：(type: ( 'change' | 'error' ), handler: 回调方法, options: 绑定配置)
 *  @property {Function}    off           解绑全屏状态更变成功/出错的回调事件。参数：(type: ( 'change' | 'error' ), handler: 回调方法, options: 绑定配置)
 *  @property {Object}      raw           全屏控件支持的原生API名称
 */

const instance = Object.create(null)

const nativeAPI = Object.create(null)
if (typeof document !== 'undefined') {
  const keys = [
    // Common
    ['fullscreenEnabled', 'fullscreenElement', 'fullscreenchange', 'fullscreenerror', 'requestFullscreen', 'exitFullscreen', ':fullscreen'],
    // WebKit
    ['webkitFullscreenEnabled', 'webkitFullscreenElement', 'webkitfullscreenchange', 'webkitfullscreenerror', 'webkitRequestFullscreen', 'webkitExitFullscreen', ':-webkit-full-screen'],
    // Mozilla
    ['mozFullScreenEnabled', 'mozFullScreenElement', 'mozfullscreenchange', 'mozfullscreenerror', 'mozRequestFullScreen', 'mozCancelFullScreen', ':-moz-full-screen'],
    // IE
    ['msFullscreenEnabled', 'msFullscreenElement', 'MSFullscreenChange', 'MSFullscreenError', 'msRequestFullscreen', 'msExitFullscreen', ':-ms-fullscreen']
  ]
  for (let i = 0; i < keys.length; i++) {
    if (keys[i][0] in document) {
      for (let j = 0; j < keys[i].length; j++) nativeAPI[keys[0][j]] = keys[i][j]
      break
    }
  }
}

const proxyKeydown = function (e) {
  if (e.keyCode === 122 && !e.altKey && !e.ctrlKey && !e.shiftKey) {
    e.preventDefault(), e.stopPropagation(), instance.toggle()
  }
}
proxyKeydown.actived = false

Object.defineProperties(instance, {
  isEnabled: {
    enumerable: true,
    get() { return Boolean(document[nativeAPI.fullscreenEnabled]) }
  },
  isFullscreen: {
    enumerable: true,
    get() { return Boolean(document[nativeAPI.fullscreenElement]) },
    set(value) { typeof value === 'boolean' && instance.toggle(value) }
  },
  proxyKeydown: {
    enumerable: true,
    get() { return proxyKeydown.actived },
    set(value) {
      if ((value = !!value) === proxyKeydown.actived) return
      window[value ? 'addEventListener' : 'removeEventListener']('keydown', proxyKeydown, true)
      proxyKeydown.actived = value
    }
  },
  element: {
    enumerable: true,
    get() { return document[nativeAPI.fullscreenElement] || null },
    set(value) { instance.toggle(value != null, value) }
  },
  request: {
    enumerable: true,
    value: function request(element, options) {
      try {
        const e = (
          element === void 0 ? document.documentElement :
          typeof element === 'string' ? document.querySelector(element) : element
        )[nativeAPI.requestFullscreen](options)
        return e instanceof Promise ? e : Promise.resolve(e)
      } catch (e) {
        return Promise.reject(e)
      }
    }
  },
  exit: {
    enumerable: true,
    value: function exit() {
      try {
        const e = document[nativeAPI.exitFullscreen]()
        return e instanceof Promise ? e : Promise.resolve(e)
      } catch (e) {
        return Promise.reject(e)
      }
    }
  },
  toggle: {
    enumerable: true,
    value: function toggle(valid, element, options) {
      if (typeof valid === 'string' || valid instanceof HTMLElement) options = element, element = valid, valid = null
      return (valid != null ? valid : !instance.isFullscreen) ? instance.request(element, options) : instance.exit()
    }
  },
  onchange: {
    enumerable: true,
    get() { return document['on' + nativeAPI.fullscreenchange] },
    set(value) { document['on' + nativeAPI.fullscreenchange] = value }
  },
  onerror: {
    enumerable: true,
    get() { return document['on' + nativeAPI.fullscreenerror] },
    set(value) { document['on' + nativeAPI.fullscreenerror] = value }
  },
  on: {
    enumerable: true,
    value: function on(type, handler, options) {
      const eventName = nativeAPI[type == 'change' ? 'fullscreenchange' : type == 'error' ? 'fullscreenerror' : '']
      if (eventName) document.addEventListener(eventName, handler, options)
    }
  },
  off: {
    enumerable: true,
    value: function off(type, handler, options) {
      const eventName = nativeAPI[type == 'change' ? 'fullscreenchange' : type == 'error' ? 'fullscreenerror' : '']
      if (eventName) document.removeEventListener(eventName, handler, options)
    }
  },
  raw: {
    enumerable: true,
    value: Object.freeze(nativeAPI)
  }
})

export default instance
